package kim.artemis.rpc.core.protocol.client;

import kim.artemis.rpc.core.annotation.ArtemisRpcReference;
import kim.artemis.rpc.core.common.enums.ServiceRegistryEnum;
import kim.artemis.rpc.core.common.exception.ArtemisException;
import kim.artemis.rpc.core.registry.ServiceRegistry;
import kim.artemis.rpc.core.protocol.serializer.ArtemisSerializer;
import kim.artemis.rpc.core.common.enums.SerializerEnum;
import kim.artemis.rpc.core.util.ArtemisUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;

/**
 * 客户端工厂
 * @author Wq.
 */
@Slf4j
public class ArtemisRpcClientFactory extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean,DisposableBean, BeanFactoryAware {

    /***
     * 服务端地址
     */
    @Setter
    private String address;

    /**
     * 序列化方式
     */
    @Setter
    private String serializer = SerializerEnum.Kryo.name();

    /**
     * 序列化
     */
    private ArtemisSerializer artemisSerializer;

    /**
     * 调用程序工厂
     */
    private ArtemisRpcClientInvoker invokerFactory;

    /**
     * 服务注册方式
     * 默认 Zookeeper
     */
    @Setter
    private String registry = ServiceRegistryEnum.Zookeeper.name();

    /**
     * 初始化客户端
     */
    public void initClient () {
        // 初始化序列化方式
        SerializerEnum serializerEnum = SerializerEnum.match(serializer, null);
        artemisSerializer = null != serializerEnum ? serializerEnum.getSerializer() : null;

        // 初始化注册中心
        ServiceRegistryEnum serviceRegistryEnum = ServiceRegistryEnum.match(registry, null);
        Class<? extends ServiceRegistry> serviceRegistry = null != serviceRegistryEnum ? serviceRegistryEnum.getServiceRegistry(): null;

        invokerFactory = new ArtemisRpcClientInvoker(serviceRegistry);
        invokerFactory.start(address);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

    }

    @Override
    public void destroy() throws Exception {
        invokerFactory.stop();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initClient();
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // serviceKey 集合
        final Set<String> serviceKeyList = new HashSet<>();

        ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                if (field.isAnnotationPresent(ArtemisRpcReference.class)) {
                    // 验证
                    Class classz = field.getType();
                    if (!classz.isInterface()) {
                        throw new ArtemisException("artemis rpc, reference(ArtemisRpcReference) must be interface.");
                    }
                    // 获取数据
                    ArtemisRpcReference artemisRpcReference = field.getAnnotation(ArtemisRpcReference.class);
                    // 初始化 artemisRpcReferenceBean
                    ArtemisRpcReferenceBean referenceBean = new ArtemisRpcReferenceBean(
                            classz,
                            artemisRpcReference.version(),
                            artemisRpcReference.serviceAddress(),
                            artemisRpcReference.timeOut(),
                            artemisRpcReference.accessToken(),
                            artemisSerializer,
                            artemisRpcReference.loadBalance(),
                            invokerFactory
                    );

                    Object serviceProxy = referenceBean.invoker();

                    field.setAccessible(true);
                    field.set(bean, serviceProxy);
                    log.info(">>>>>>> artemis rpc, invoker factory init reference bean successful. serviceKey = {}, bean.field = {}.{}",
                            ArtemisUtils.serviceKey(classz.getName(), artemisRpcReference.version()), beanName, field.getName());
                    String serviceKey = ArtemisUtils.serviceKey(classz.getName(), artemisRpcReference.version());
                    serviceKeyList.add(serviceKey);
                }
            }
        });

        if (null != invokerFactory.getServiceRegistry()) {
            try {
                // 发现服务
                invokerFactory.getServiceRegistry().discovery(serviceKeyList);
            } catch (Exception e) {
                log.error(">>>>>>> artemis rpc, no service found.{}", e.getMessage());
            }
        }

        return super.postProcessAfterInitialization(bean, beanName);
    }
}
